mirror of
https://github.com/xweiba/location-spoofer.git
synced 2026-09-26 00:21:55 +08:00
feat: improve runtime setup and guidance
This commit is contained in:
+133
-41
@@ -13,6 +13,7 @@ struct SettingsView: View {
|
||||
@State private var proxyOperationAlertTitle = "代理操作失败"
|
||||
@State private var modeOperationRunning = false
|
||||
@State private var copiedClient: ThirdPartyProxyClient?
|
||||
@State private var showCertificateResetConfirmation = false
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
@@ -25,11 +26,6 @@ struct SettingsView: View {
|
||||
.pickerStyle(.inline)
|
||||
.disabled(modeOperationRunning || actions.state.isBusy || thirdPartyProxy.isRequesting)
|
||||
|
||||
if runtimeMode.mode == .thirdParty {
|
||||
Label("测试模式:仅 Shadowrocket 当前可测试", systemImage: "testtube.2")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.orange)
|
||||
}
|
||||
}
|
||||
|
||||
Section("状态") {
|
||||
@@ -69,22 +65,23 @@ struct SettingsView: View {
|
||||
thirdPartyConfigurationSection
|
||||
} else {
|
||||
Section("说明") {
|
||||
Button {
|
||||
activeTip = .activation
|
||||
} label: {
|
||||
Label("生效说明", systemImage: "checklist")
|
||||
}
|
||||
Button {
|
||||
activeTip = .deactivation
|
||||
} label: {
|
||||
Label("失效说明", systemImage: "arrow.uturn.backward.circle")
|
||||
}
|
||||
Button {
|
||||
activeTip = .removeProxy
|
||||
} label: {
|
||||
Label("关闭 WiFi 代理", systemImage: "wifi.slash")
|
||||
}
|
||||
Button {
|
||||
activeTip = .activation
|
||||
} label: {
|
||||
Label("生效说明", systemImage: "checklist")
|
||||
}
|
||||
Button {
|
||||
activeTip = .deactivation
|
||||
} label: {
|
||||
Label("失效说明", systemImage: "arrow.uturn.backward.circle")
|
||||
}
|
||||
Button {
|
||||
activeTip = .removeProxy
|
||||
} label: {
|
||||
Label("关闭 WiFi 代理", systemImage: "wifi.slash")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Section("工作原理") {
|
||||
@@ -104,6 +101,21 @@ struct SettingsView: View {
|
||||
valueRow("版本", value: versionText)
|
||||
}
|
||||
|
||||
if runtimeMode.mode == .localWiFi {
|
||||
Section("证书") {
|
||||
Button(role: .destructive) {
|
||||
showCertificateResetConfirmation = true
|
||||
} label: {
|
||||
Label("重置证书", systemImage: "arrow.clockwise.circle")
|
||||
}
|
||||
.disabled(modeOperationRunning || actions.state.isBusy)
|
||||
|
||||
Text("仅删除 App 钥匙串中的设备 CA。iOS 中已经安装的旧证书需要在系统设置里手动移除。")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
Section("支持") {
|
||||
NavigationLink {
|
||||
BugReportView(setup: setup)
|
||||
@@ -152,6 +164,18 @@ struct SettingsView: View {
|
||||
} message: {
|
||||
Text(proxyOperationError)
|
||||
}
|
||||
.confirmationDialog(
|
||||
"重置证书?",
|
||||
isPresented: $showCertificateResetConfirmation,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("重置并生成新证书", role: .destructive) {
|
||||
resetCertificateAuthority()
|
||||
}
|
||||
Button("取消", role: .cancel) {}
|
||||
} message: {
|
||||
Text("当前虚拟定位和本地代理将停止。App 会删除钥匙串中的设备 CA、立即生成新证书,并打开安装与信任引导。你还需要前往 iOS「设置 → 通用 → VPN 与设备管理」手动删除旧证书,然后重新下载安装并完全信任新证书。")
|
||||
}
|
||||
}
|
||||
|
||||
private func valueRow(_ title: String, value: String) -> some View {
|
||||
@@ -205,19 +229,21 @@ struct SettingsView: View {
|
||||
}
|
||||
}
|
||||
|
||||
HStack {
|
||||
Text("验证状态")
|
||||
Spacer()
|
||||
Text(thirdPartyClient.selectedClient.verificationText)
|
||||
.font(.footnote)
|
||||
.foregroundStyle(thirdPartyClient.selectedClient == .shadowrocket ? .green : .orange)
|
||||
if let verificationText = thirdPartyClient.selectedClient.verificationText {
|
||||
HStack {
|
||||
Text("验证状态")
|
||||
Spacer()
|
||||
Text(verificationText)
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.orange)
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
UIPasteboard.general.string = thirdPartyClient.selectedClient.subscriptionURL.absoluteString
|
||||
copiedClient = thirdPartyClient.selectedClient
|
||||
} label: {
|
||||
Label(copiedClient == thirdPartyClient.selectedClient ? "已复制订阅链接" : "复制订阅链接", systemImage: "doc.on.doc")
|
||||
Label(copiedClient == thirdPartyClient.selectedClient ? "已复制模块订阅地址" : "复制模块订阅地址", systemImage: "doc.on.doc")
|
||||
}
|
||||
|
||||
Button {
|
||||
@@ -227,7 +253,7 @@ struct SettingsView: View {
|
||||
}
|
||||
|
||||
Button {
|
||||
setup.requestThirdPartySetup()
|
||||
setup.requestThirdPartyOnboarding()
|
||||
dismiss()
|
||||
} label: {
|
||||
Label("重新打开配置引导", systemImage: "arrow.clockwise.circle")
|
||||
@@ -241,7 +267,7 @@ struct SettingsView: View {
|
||||
.font(.footnote).foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
Text("复制链接后,在对应代理客户端中添加模块/重写订阅,并启用 MITM。第三方客户端保存坐标后,即使关闭本 App,坐标仍由代理客户端持久化并继续生效。")
|
||||
Text("复制模块订阅地址后,在对应代理客户端中添加模块/重写订阅,并启用 MITM。第三方客户端保存坐标后,即使关闭本 App,坐标仍由代理客户端持久化并继续生效。")
|
||||
.font(.footnote).foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
@@ -294,9 +320,18 @@ struct SettingsView: View {
|
||||
proxy.stop()
|
||||
setup.completeSetup()
|
||||
runtimeMode.setMode(.thirdParty)
|
||||
setup.requestThirdPartySetup()
|
||||
proxyOperationAlertTitle = "模式已切换"
|
||||
proxyOperationError = "已切换到第三方代理模式。请关闭 Wi-Fi 中的 127.0.0.1:8888 手动代理,并按引导导入第三方配置。"
|
||||
if runtimeMode.isInitialized(.thirdParty) {
|
||||
do {
|
||||
_ = try await thirdPartyProxy.query()
|
||||
proxyOperationAlertTitle = "模式已切换"
|
||||
proxyOperationError = "第三方代理模式检测通过。请关闭 Wi-Fi 中的 127.0.0.1:8888 手动代理,避免双重拦截。"
|
||||
} catch {
|
||||
openThirdPartySetup(for: error)
|
||||
}
|
||||
} else {
|
||||
setup.requestThirdPartyOnboarding()
|
||||
dismiss()
|
||||
}
|
||||
case .localWiFi:
|
||||
do {
|
||||
try await thirdPartyProxy.clear()
|
||||
@@ -307,23 +342,80 @@ struct SettingsView: View {
|
||||
}
|
||||
runtimeMode.setMode(.localWiFi)
|
||||
await setup.prepareLocalServices()
|
||||
setup.requestSetup()
|
||||
proxyOperationAlertTitle = "模式已切换"
|
||||
proxyOperationError = "已切换到 APP 模式。请停用第三方 WLOC 模块或代理连接,避免双重拦截。"
|
||||
if runtimeMode.isInitialized(.localWiFi) {
|
||||
let result = await setup.runVerificationTest()
|
||||
setup.applyVerificationResult(result)
|
||||
if result.isSuccess {
|
||||
proxyOperationAlertTitle = "模式已切换"
|
||||
proxyOperationError = "APP 模式环境检测通过。请停用第三方 WLOC 模块或代理连接,避免双重拦截。"
|
||||
} else {
|
||||
dismiss()
|
||||
}
|
||||
} else {
|
||||
setup.requestSetup()
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func detectThirdPartyConnection() {
|
||||
let client = thirdPartyClient.selectedClient
|
||||
let startedAt = Date()
|
||||
Task { @MainActor in
|
||||
do {
|
||||
let response = try await thirdPartyProxy.query()
|
||||
if !response.success, response.error?.contains("无已保存") != true {
|
||||
proxyOperationAlertTitle = "检测失败"
|
||||
proxyOperationError = response.error ?? "第三方代理模块返回失败"
|
||||
}
|
||||
_ = try await thirdPartyProxy.query()
|
||||
RuntimeLogger.info("APP", "ThirdPartyProxy", "设置页第三方连接检测通过", details: [
|
||||
"当前客户端": client.name,
|
||||
"请求动作": "WLOC query",
|
||||
"耗时毫秒": String(Int(Date().timeIntervalSince(startedAt) * 1_000))
|
||||
])
|
||||
runtimeMode.markInitialized(.thirdParty)
|
||||
} catch {
|
||||
proxyOperationAlertTitle = "检测失败"
|
||||
RuntimeLogger.error(
|
||||
"APP",
|
||||
"ThirdPartyProxy",
|
||||
"设置页第三方连接检测失败",
|
||||
error: error,
|
||||
details: [
|
||||
"当前客户端": client.name,
|
||||
"请求动作": "WLOC query",
|
||||
"连接状态": String(describing: thirdPartyProxy.connectionState),
|
||||
"耗时毫秒": String(Int(Date().timeIntervalSince(startedAt) * 1_000)),
|
||||
"处理建议": "检查模块、MITM、证书和代理/VPN连接"
|
||||
]
|
||||
)
|
||||
openThirdPartySetup(for: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func openThirdPartySetup(for error: Error) {
|
||||
setup.requestThirdPartySetup(message: error.localizedDescription)
|
||||
dismiss()
|
||||
}
|
||||
|
||||
private func resetCertificateAuthority() {
|
||||
guard runtimeMode.mode == .localWiFi, !modeOperationRunning else { return }
|
||||
modeOperationRunning = true
|
||||
Task { @MainActor in
|
||||
defer { modeOperationRunning = false }
|
||||
if actions.virtualLocationEnabled {
|
||||
actions.clear()
|
||||
}
|
||||
proxy.stop()
|
||||
do {
|
||||
try setup.certificateStore.reset()
|
||||
runtimeMode.resetInitialization(.localWiFi)
|
||||
guard await setup.prepareLocalServices() else {
|
||||
proxyOperationAlertTitle = "证书重置失败"
|
||||
proxyOperationError = setup.message
|
||||
return
|
||||
}
|
||||
setup.requestCertificateSetup()
|
||||
dismiss()
|
||||
} catch {
|
||||
proxyOperationAlertTitle = "证书重置失败"
|
||||
proxyOperationError = error.localizedDescription
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user